<template>
  <el-col v-if="$props.show" v-bind="getColProps">
    <FormItem v-bind="getBindItemValue" :style="{ marginBottom: $props.formItemMb }">
      <template v-if="$props.slots">
        <slot :name="$props.slots" v-bind="getBindValues" />
      </template>
      <component
        :is="getComp"
        v-else
        v-bind="getBindValues"
        @input="handleChange"
        @enter="handleEnterSearch"
        @options-change="handleOptChange"
      />
    </FormItem>
  </el-col>
</template>

<script setup name="basic-from-item">
import { ref, computed, watch, unref, useAttrs } from 'vue'
import { FormItem } from 'element-ui'
import { isFunction, omit } from 'lodash-es'
import { fromItemProps } from '../props'
import { componentMap } from '../componentMap'
import { createPlaceholderMessage } from '../helper'

const props = defineProps(fromItemProps)
const emit = defineEmits(['change', 'enter'])

const attrs = useAttrs()

const val = ref(props.defaultValue)
const firstLoad = ref(false) // 是否是初次渲染

const getColProps = computed(() => {
  const { colProps, component } = props
  const span = component === 'Divider' || component === 'Title' ? 24 : colProps.span
  return {
    span,
    ...omit(colProps, 'span')
  }
})

watch(
  () => props.value,
  value => {
    val.value = value
  },
  {
    immediate: true
  }
)

const getBindItemValue = computed(() => {
  const { colon, label, component, labelWidth } = props
  let labelWd = labelWidth
  let title = colon ? `${label}:` : label
  if (component === 'Divider' || component === 'Title') {
    labelWd = '0px'
    title = ''
  }
  const opt = omit(
    props,
    'componentProps',
    'colProps',
    'colon',
    'defaultValue',
    'show',
    'component',
    'formData',
    'formItemMb'
  )
  const rules = validate()

  return {
    ...opt,
    label: title,
    rules,
    value: unref(val),
    labelWidth: labelWd
  }
})

const getComp = computed(() => {
  if (props?.renderContent && isFunction(props.renderContent)) {
    return props.renderContent(props)
  }
  return componentMap.get(props.component)
})

const getBindValues = computed(() => {
  const compProps = props.componentProps || {}
  const label = props.label
  const prop = props.prop

  const placeholder = compProps?.placeholder ?? createPlaceholderMessage(props.component, props.label)
  return { value: unref(val), clearable: true, ...attrs, placeholder, label, prop, ...compProps }
})

function validate() {
  let rules = []
  const required = props.required && props.rules.length === 0
  if (required) {
    const { label } = props
    rules = [{ required, message: `${label}必填` }]
  } else {
    rules = props.rules || []
  }
  return rules
}

function handleChange(e) {
  val.value = e
  // 初始渲染不触发 change 事件
  // InputNumber 类型的输入框，如果设置默认值，初始化时会触发 input 事件
  if (!unref(firstLoad)) {
    emit('change', { ...props, value: e })
    firstLoad.value = false
  }
  unref(getBindValues)?.onChange && unref(getBindValues)?.onChange(e, attrs?.formData ?? {})
}

function handleEnterSearch(e) {
  emit('enter', { ...props, value: e })
}

function handleOptChange(e) {
  emit('change', { ...props, value: e })
  unref(getBindValues)?.onChange && unref(getBindValues)?.onChange(e)
}
</script>

<style lang="scss" scoped>
.basic-from-item {
  flex: 1;
}

::v-deep {
  .el-date-editor.el-input,
  .el-date-editor.el-input__inner {
    width: 100%;
  }

  .el-select,
  .el-cascader,
  .vxe-pulldown {
    width: 100% !important;
  }

  .el-form-item__label {
    padding-left: 16px;
  }
}
</style>
